/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2021 Guanzhong Chen <quantum2048@gmail.com>
https://looking-glass.io

This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation; either version 2 of the License, or (at your option) any later
version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
Place, Suite 330, Boston, MA 02111-1307 USA
*/

#include "help.h"
#include "common/debug.h"

#include "texture.h"
#include "shader.h"
#include "model.h"

#include <stdatomic.h>
#include <stdlib.h>
#include <string.h>

// these headers are auto generated by cmake
#include "help.vert.h"
#include "help.frag.h"
#include "help_bg.frag.h"

struct EGL_Help
{
  const LG_Font * font;
  LG_FontObj      fontObj;

  EGL_Texture * texture;
  EGL_Shader  * shader;
  EGL_Shader  * shaderBG;
  EGL_Model   * model;

  _Atomic(LG_FontBitmap *) bmp;

  bool  shouldRender;
  int   iwidth, iheight;
  float width, height;

  // uniforms
  GLint uScreen  , uSize;
  GLint uScreenBG, uSizeBG;
};

bool egl_help_init(EGL_Help ** help, const LG_Font * font, LG_FontObj fontObj)
{
  *help = (EGL_Help *)malloc(sizeof(EGL_Help));
  if (!*help)
  {
    DEBUG_ERROR("Failed to malloc EGL_Help");
    return false;
  }

  memset(*help, 0, sizeof(EGL_Help));

  (*help)->font    = font;
  (*help)->fontObj = fontObj;

  if (!egl_texture_init(&(*help)->texture, NULL))
  {
    DEBUG_ERROR("Failed to initialize the help texture");
    return false;
  }

  if (!egl_shader_init(&(*help)->shader))
  {
    DEBUG_ERROR("Failed to initialize the help shader");
    return false;
  }

  if (!egl_shader_init(&(*help)->shaderBG))
  {
    DEBUG_ERROR("Failed to initialize the help bg shader");
    return false;
  }


  if (!egl_shader_compile((*help)->shader,
        b_shader_help_vert, b_shader_help_vert_size,
        b_shader_help_frag, b_shader_help_frag_size))
  {
    DEBUG_ERROR("Failed to compile the help shader");
    return false;
  }

  if (!egl_shader_compile((*help)->shaderBG,
        b_shader_help_vert   , b_shader_help_vert_size,
        b_shader_help_bg_frag, b_shader_help_bg_frag_size))
  {
    DEBUG_ERROR("Failed to compile the help shader");
    return false;
  }

  (*help)->uSize     = egl_shader_get_uniform_location((*help)->shader  , "size"  );
  (*help)->uScreen   = egl_shader_get_uniform_location((*help)->shader  , "screen");
  (*help)->uSizeBG   = egl_shader_get_uniform_location((*help)->shaderBG, "size"  );
  (*help)->uScreenBG = egl_shader_get_uniform_location((*help)->shaderBG, "screen");

  if (!egl_model_init(&(*help)->model))
  {
    DEBUG_ERROR("Failed to initialize the fps model");
    return false;
  }

  egl_model_set_default((*help)->model);
  egl_model_set_texture((*help)->model, (*help)->texture);

  atomic_init(&(*help)->bmp, NULL);

  return true;
}

void egl_help_free(EGL_Help ** help)
{
  if (!*help)
    return;

  egl_texture_free(&(*help)->texture );
  egl_shader_free (&(*help)->shader  );
  egl_shader_free (&(*help)->shaderBG);
  egl_model_free  (&(*help)->model   );

  free(*help);
  *help = NULL;
}

void egl_help_set_text(EGL_Help * help, const char * help_text)
{
  LG_FontBitmap * bmp = NULL;
  if (help_text)
  {
    bmp = help->font->render(help->fontObj, 0xffffff00, help_text);
    if (!bmp)
      DEBUG_ERROR("Failed to render help text");
  } else
    help->shouldRender = false;

  bmp = atomic_exchange(&help->bmp, bmp);
  if (bmp)
  {
    help->font->release(help->fontObj, bmp);
  }
}

void egl_help_set_font(EGL_Help * help, LG_FontObj fontObj)
{
  help->fontObj = fontObj;
}

void egl_help_render(EGL_Help * help, const float scaleX, const float scaleY)
{
  LG_FontBitmap * bmp = atomic_exchange(&help->bmp, NULL);
  if (bmp)
  {
    if (help->iwidth != bmp->width || help->iheight != bmp->height)
    {
      help->iwidth  = bmp->width;
      help->iheight = bmp->height;
      help->width   = (float)bmp->width;
      help->height  = (float)bmp->height;

      egl_texture_setup(
        help->texture,
        EGL_PF_BGRA,
        bmp->width ,
        bmp->height,
        bmp->width * bmp->bpp,
        false,
        false
      );
    }

    egl_texture_update
    (
      help->texture,
      bmp->pixels
    );

    help->shouldRender = true;
    help->font->release(help->fontObj, bmp);
  }

  if (!help->shouldRender)
    return;

  glEnable(GL_BLEND);
  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

  // render the background first
  egl_shader_use(help->shaderBG);
  glUniform2f(help->uScreenBG, scaleX     , scaleY      );
  glUniform2f(help->uSizeBG  , help->width, help->height);
  egl_model_render(help->model);

  // render the texture over the background
  egl_shader_use(help->shader);
  glUniform2f(help->uScreen, scaleX     , scaleY      );
  glUniform2f(help->uSize  , help->width, help->height);
  egl_model_render(help->model);

  glDisable(GL_BLEND);
}
